package gonet

import (
	"fmt"
	"git.gz.internal.jumaiyx.cn/pkg/tnet/pack"
	"git.gz.internal.jumaiyx.cn/pkg/tnet/request"
	"git.gz.internal.jumaiyx.cn/pkg/tnet/route"
	"github.com/panjf2000/gnet/v2"
	"github.com/panjf2000/gnet/v2/pkg/pool/goroutine"
	"time"
)

type Server interface {
	AddRoute(msgId uint32, rt route.IRouter)
	Start()
}
type hb struct {
	interval time.Duration // 心跳间隔时间
	msgID    uint32        // 心跳的接受msgId
	router   route.IRouter // 心跳的路由
}
type server struct {
	protocol    pack.Protocol
	addr        string
	maxConn     int32
	currentConn int32
	apis        map[uint32]route.IRouter
	heartbeat   hb
	onStart     func(gnet.Conn)
	onStop      func(gnet.Conn)
	onIntercept func(request.IRequest) error
}

type Option func(*server)

func Protocol(n pack.Protocol) Option {
	return func(s *server) {
		s.protocol = n
	}
}

func Addr(addr string) Option {
	return func(s *server) {
		s.addr = addr
	}
}

func MaxConn(max int32) Option {
	return func(s *server) {
		s.maxConn = max
	}
}

// OnStart 客户端建立连接时执行的方法
func OnStart(conn func(gnet.Conn)) Option {
	return func(s *server) {
		s.onStart = conn
	}
}

// OnStop 客户端断开连接时执行的方法
func OnStop(conn func(gnet.Conn)) Option {
	return func(s *server) {
		s.onStop = conn
	}
}

// OnIntercept 拦截
func OnIntercept(intercept func(request.IRequest) error) Option {
	return func(s *server) {
		s.onIntercept = intercept
	}
}

// Heartbeat UDP不用设置
func Heartbeat(msgID uint32, router route.IRouter, interval time.Duration) Option {
	return func(s *server) {
		s.heartbeat = hb{
			msgID:    msgID,
			router:   router,
			interval: interval,
		}
		s.apis[msgID] = router
	}
}

func NewServer(options ...Option) Server {
	s := &server{
		apis:      make(map[uint32]route.IRouter),
		maxConn:   100,
		protocol:  pack.TCP,
		addr:      ":7070",
		heartbeat: hb{},
	}
	for _, option := range options {
		option(s)
	}
	return s
}

func (server *server) Start() {
	var (
		addr         string
		eventHandler gnet.EventHandler
	)
	switch server.protocol {
	case pack.UDP:
		addr = string(pack.UDP) + "://" + server.addr
		eventHandler = &UdpServer{
			pool:          goroutine.Default(),
			apis:          server.apis,
			connStart:     server.onStart,
			connStop:      server.onStop,
			connIntercept: server.onIntercept,
		}
		fmt.Println("udp:", addr)
		break
	default:
		addr = string(pack.TCP) + "://" + server.addr
		fmt.Println("tcp:", addr)
		eventHandler = &TcpServer{
			pool:          goroutine.Default(),
			apis:          server.apis,
			heartbeat:     server.heartbeat,
			connStart:     server.onStart,
			connStop:      server.onStop,
			connIntercept: server.onIntercept,
		}
		break
	}

	err := gnet.Run(eventHandler, addr,
		gnet.WithLockOSThread(true),          // 为I/O事件循环设置LockOSThread模式。
		gnet.WithMulticore(true),             // 在gnet引擎中设置多核心。
		gnet.WithReusePort(false),            // 设置SO_REUSEPORT套接字选项。本选项允许完全重复的捆绑，不过只有在想要捆绑同一IP地址和端口的每个套接字都指定了本套接字选项才行
		gnet.WithReuseAddr(false),            // 设置SO_REUSEADDR套接字选项。SO_REUSEADDR允许在同一端口上启动同一服务器的多个实例，只要每个实例捆绑一个不同的本地IP地址即可。
		gnet.WithTicker(true),                // 表示设置了自动收报机。
		gnet.WithTCPKeepAlive(time.Minute*1), // 设置带有持续时间的SO_KEEPALIVE套接字选项。
		gnet.WithTCPNoDelay(gnet.TCPDelay),   // 启用/禁用TCP_NODELAY套接字选项。
		gnet.WithLoadBalancing(gnet.RoundRobin))
	if err != nil {
		panic(err)
	}
}

func (server *server) AddRoute(msgId uint32, rt route.IRouter) {
	if _, ok := server.apis[msgId]; ok {
		panic(fmt.Sprintf("repeated route, msgId=%d", msgId))
	}
	server.apis[msgId] = rt
}
